package com.hanson.common.util;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSSClient;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 文件上传到oss
 *
 * @author Administrator
 */
@Slf4j
public class FileUploadUtil {
    private static final String endPointInner = "http://oss-cn-beijing-internal.aliyuncs.com";
    private static final String endPointOut = "http://oss-cn-beijing.aliyuncs.com";
    private static final String accessKeyId = "LTAI4G3GVEbbqpGTKw8dzWSy";
    private static final String accessKeySecret = "9rgLHbICo711FZFMuugQ824rZGrZMX";
    private static final String bucketName = "zhizhikeji";

    private static final String Inner = "oss-cn-beijing-internal.aliyuncs.com";
    private static final String Outer = "oss-cn-beijing.aliyuncs.com";

    public static OSSClient ossClient = null;

    public static String uploadFileSystem(String isLocal, String filePath) throws Exception {
        File file = new File(filePath);
        String fileUrl = null;
        if (file.exists()) {
            InputStream in = new FileInputStream(file);
            fileUrl = upload(isLocal, file.getName(), in);
        } else {
            log.error("文件不存在 filePath:{}", filePath);
        }
        return fileUrl;
    }


    public static String upload(String isLocal, String fileName, InputStream in) {
        long start = System.currentTimeMillis();
        // 创建OSSClient实例
        OSSClient ossClient = new OSSClient((StringUtils.isNotEmpty(isLocal) && "1".equals(isLocal)) ? endPointOut : endPointInner, accessKeyId, accessKeySecret);
        ossClient.putObject(bucketName, fileName, in);

        Date date = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.YEAR, 30);
        // 生成URL
        URL url = ossClient.generatePresignedUrl(bucketName, fileName, new Date(cal.getTimeInMillis()));
        String filePath = url.toString();

        // 使用访问OSS
        // 关闭ossClient
        ossClient.shutdown();
        long end = System.currentTimeMillis();
        System.out.println("isLocal=" + isLocal + ",上传文件 所用时间：" + (end - start));
        return (StringUtils.isNotEmpty(isLocal) && "1".equals(isLocal)) ? filePath : innerToOutUrl(filePath);
    }

    private static String innerToOutUrl(String url) {
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        return url.toString().replace(Inner, Outer);
    }

    public static JSONObject uploadOssFile(HttpServletRequest request, String isLocal) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                request.getSession().getServletContext());
        multipartResolver.setDefaultEncoding("UTF-8");
        JSONObject jsonObject = new JSONObject();
        try {
            if (multipartResolver.isMultipart(request)) {
                MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
                Iterator iter = multiRequest.getFileNames();
                while (iter.hasNext()) {
                    MultipartFile file = multiRequest.getFile((String) iter.next());
                    String originalFilename = new String(file.getOriginalFilename().getBytes("ISO-8859-1"), "UTF-8");
                    if ((file == null) || (originalFilename.length() == 0)) {
                        continue;
                    }
                    InputStream in = null;
                    if (!originalFilename.endsWith("mp4")) {
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
                        Thumbnails.of(file.getInputStream()).scale(1f).outputQuality(0.5f).toOutputStream(out);//压缩图片
                        in = new ByteArrayInputStream(out.toByteArray());
                    } else {
                        in = file.getInputStream();
                    }

                    String uploadFileName = UUID.randomUUID().toString().replaceAll("-", "") + originalFilename.substring(originalFilename.lastIndexOf("."));
                    String url = upload(isLocal, uploadFileName, in);
                    // String url = uploadNew(file, isLocal);
                    jsonObject.put("src", url);
                    jsonObject.put("fileName", originalFilename);
                    jsonObject.put("fileSize", file.getSize());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public synchronized static String uploadNew(MultipartFile file, String isLocal) {
        long beginTime = System.currentTimeMillis();
        OSSClient client = new OSSClient((StringUtils.isNotEmpty(isLocal) && "1".equals(isLocal)) ? endPointOut : endPointInner, accessKeyId, accessKeySecret);// 根据阿里云用户配置创建连接对象实例
        String url = "";
        try {
            ExecutorService executorService = Executors.newFixedThreadPool(50);
            String key = new String(file.getOriginalFilename().getBytes("ISO-8859-1"), "UTF-8");
            String uploadId = AliyunOSSUpload.claimUploadId(bucketName, key, client);// key是文件名
            // 设置每块为 5M(除最后一个分块以外，其他的分块大小都要大于5MB)
            final long partSize = 4 * 1024 * 1024L;
            // 计算分块数目
            long fileLength = file.getSize();// 文件大小
            int partCount = (int) (fileLength / partSize);// 文件大小%分块大小
            if (fileLength % partSize != 0) {
                partCount++;
            }

            // 分块 号码的范围是1~10000。如果超出这个范围，OSS将返回InvalidArgument的错误码。
            if (partCount > 10000) {
                throw new RuntimeException("文件过大(分块大小不能超过10000)");
            } else {
                System.out.println("一共分了 " + partCount + " 块");
            }

            /**
             * 将分好的文件块加入到list集合中
             */
            for (int i = 0; i < partCount; i++) {
                // 起始point
                long startPos = i * partSize;
                // 判断当前partSize的长度 是否最后一块
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                // 线程执行。将分好的文件块加入到list集合中()
                executorService.execute(
                        new AliyunOSSUpload(file, startPos, curPartSize, i + 1, uploadId, key, bucketName, client));
            }

            /**
             * 等待所有分片完毕
             */
            // 关闭线程池（线程池不马上关闭），执行以前提交的任务，但不接受新任务。
            executorService.shutdown();
            // 如果关闭后所有任务都已完成，则返回 true。
            while (!executorService.isTerminated()) {
                try {
                    // 用于等待子线程结束，再继续执行下面的代码
                    executorService.awaitTermination(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            /**
             * partETags(上传块的ETag与块编号（PartNumber）的组合) 如果校验与之前计算的分块大小不同，则抛出异常
             */
            System.out.println(AliyunOSSUpload.partETags.size() + " -----   " + partCount);
            if (AliyunOSSUpload.partETags.size() != partCount) {
                throw new IllegalStateException("OSS分块大小与文件所计算的分块大小不一致");
            } else {
                System.out.println("将要上传的文件名  " + key + "\n");
            }

			/*
             * 列出文件所有的分块清单并打印到日志中，该方法仅仅作为输出使用
			 */
            AliyunOSSUpload.listAllParts(uploadId, client);

			/*
             * 完成分块上传
			 */
            url = AliyunOSSUpload.completeMultipartUpload(uploadId, key, client);

            // 返回上传文件的URL地址
            // return
            // endpoint.replaceFirst("http://","http://"+bucketName+".")+"/"+key
            // ;
            long endTime = System.currentTimeMillis();
            System.out.println("=====文件上传完成，所用时间：" + (endTime - beginTime) / 1000 + "秒，返回URL:" + url);
            return (StringUtils.isNotEmpty(isLocal) && "1".equals(isLocal)) ? url : innerToOutUrl(url);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            AliyunOSSUpload.partETags.clear();
            if (client != null) {
                client.shutdown();
            }
        }
        return null;
    }
}
